#ifndef _TRIANGLE_MESH_OCL_H_
#define _TRIANGLE_MESH_OCL_H_

//OpenCL side code for triangle mesh
#include "geometry.h"
#include "differential_geometry.h"
#include "ray.h"
#include "matrix4x4.h"
#include "mc.h"
#include "shape.h"

typedef struct 
{
	point3f_t p0,p1,p2;
	int reverse_orientation;
	unsigned v0_index,v1_index,v2_index;
//	vector3f_t n0,n1,n2;
//	vector3f_t s0,s1,s2;
//	float uv0[2],uv1[2],uv2[2];

}triangle_t;

INLINE void load_triangle_only_point_data_(GLOBAL const float* triangle_mesh_memory, unsigned v0_index,unsigned v1_index, unsigned v2_index,triangle_t *triangle)
{
	triangle->v0_index = v0_index;
	triangle->v1_index = v1_index;
	triangle->v2_index = v2_index;

	unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
	unsigned number_vertex = as_uint(triangle_mesh_memory[1]);
	unsigned reverse_orientation = as_uint(triangle_mesh_memory[2]);
	triangle->reverse_orientation = reverse_orientation;
	unsigned transform_start = 3;
	transform_t object_to_world;
	transform_init((object_to_world),
		triangle_mesh_memory[transform_start],triangle_mesh_memory[transform_start+1],triangle_mesh_memory[transform_start+2],triangle_mesh_memory[transform_start+3],
		triangle_mesh_memory[transform_start+4],triangle_mesh_memory[transform_start+5],triangle_mesh_memory[transform_start+6],triangle_mesh_memory[transform_start+7],
		triangle_mesh_memory[transform_start+8],triangle_mesh_memory[transform_start+9],triangle_mesh_memory[transform_start+10],triangle_mesh_memory[transform_start+11],
		triangle_mesh_memory[transform_start+12],triangle_mesh_memory[transform_start+13],triangle_mesh_memory[transform_start+14],triangle_mesh_memory[transform_start+15])
	unsigned point_start = transform_start+16;//add transform_data
	//clear triangle data
	vclr(triangle->p0)
	vclr(triangle->p1)
	vclr(triangle->p2)
	point3f_t p;
	vclr(p);

	p.x = triangle_mesh_memory[3*v0_index + point_start];
	p.y = triangle_mesh_memory[3*v0_index + 1 + point_start];
	p.z = triangle_mesh_memory[3*v0_index + 2 + point_start];
	transform_point((triangle->p0),object_to_world,p);
	
	p.x = triangle_mesh_memory[3*v1_index + point_start];
	p.y = triangle_mesh_memory[3*v1_index + 1 + point_start];
	p.z = triangle_mesh_memory[3*v1_index + 2 + point_start];
	transform_point((triangle->p1),object_to_world,p);

	p.x = triangle_mesh_memory[3*v2_index + point_start];
	p.y = triangle_mesh_memory[3*v2_index + 1 + point_start];
	p.z = triangle_mesh_memory[3*v2_index + 2 + point_start];
	transform_point((triangle->p2),object_to_world,p);
}
//////////////
INLINE void _get_tu_tv(GLOBAL const float* triangle_mesh_memory,unsigned vertex_format,const triangle_t* triangle, float b1,float b2,unsigned uvs_start,
		//out
		float* tu,float *tv,
		float* du1,float *du2,
		float* dv1,float *dv2
		)
{
	float uv0[2],uv1[2],uv2[2];
	uv0[0] = 0.f; uv0[1] = 0.f;
	uv1[0] = 1.f; uv1[1] = 0.f;
	uv2[0] = 1.f; uv2[1] = 1.f;
	if( 0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_UVS))
	{
		//load uvs
		uv0[0] = triangle_mesh_memory[2*triangle->v0_index + uvs_start];
		uv0[1] = triangle_mesh_memory[2*triangle->v0_index + 1 + uvs_start];

		uv1[0] = triangle_mesh_memory[2*triangle->v1_index + uvs_start];
		uv1[1] = triangle_mesh_memory[2*triangle->v1_index + 1 + uvs_start];

		uv2[0] = triangle_mesh_memory[2*triangle->v2_index + uvs_start];
		uv2[1] = triangle_mesh_memory[2*triangle->v2_index + 1 + uvs_start];
	}
	float b0 = 1-b1-b2;
	*tu = b0 * uv0[0] + b1*uv1[0] + b2*uv2[0];
	*tv = b0 * uv0[1] + b1*uv1[1] + b2*uv2[1];

	*du1 = uv0[0] - uv2[0];
	*du2 = uv1[0] - uv2[0];
	*dv1 = uv0[1] - uv2[1];
	*dv2 = uv1[1] - uv2[1];
}
INLINE void _get_ss_ts(GLOBAL const float* triangle_mesh_memory,unsigned vertex_format,const triangle_t* triangle, float b1,float b2,unsigned tangent_start,
		float du1,float du2,float dv1,float dv2,
		float tu,float tv,const normal3f_t* ns,
		const vector3f_t* dpdu,
		const transform_t* object_to_world,
		//out
		vector3f_t* ts,
		vector3f_t* ss
		)
{
	*ss = *dpdu;
	vnorm((*ss));
	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_TANGENT))
	{
		//load tangent
		vector3f_t s0,s1,s2;
		vclr(s0);
		vclr(s1);
		vclr(s2);

		vector3f_t s;
		vclr(s);
		s0.x = triangle_mesh_memory[3*triangle->v0_index + tangent_start];
		s0.y = triangle_mesh_memory[3*triangle->v0_index + 1 + tangent_start];
		s0.z = triangle_mesh_memory[3*triangle->v0_index + 2 + tangent_start];
		//transform_vector((s0),object_to_world,s);

		s1.x = triangle_mesh_memory[3*triangle->v1_index + tangent_start];
		s1.y = triangle_mesh_memory[3*triangle->v1_index + 1 + tangent_start];
		s1.z = triangle_mesh_memory[3*triangle->v1_index + 2 + tangent_start];
		//transform_vector((s1),object_to_world,s);

		s2.x = triangle_mesh_memory[3*triangle->v2_index + tangent_start];
		s2.y = triangle_mesh_memory[3*triangle->v2_index + 1 + tangent_start];
		s2.z = triangle_mesh_memory[3*triangle->v2_index + 2 + tangent_start];
		//transform_vector((s2),object_to_world,s);
		//
		float b0 = 1-b1-b2;
		vsmul(s0,b0,s0);
		vsmul(s1,b1,s1);
		vsmul(s2,b2,s2);
		vadd(s,s0,s1);
		vadd(s,s,s2);
		transform_vector((*ss),(*object_to_world),s);
		vnorm((*ss));
	}
	/////////////////////////////////////////////////////////////////////
	vxcross((*ts),(*ss),(*ns));
	if(vdot((*ts),(*ts)) > 0.f)
	{
		vnorm((*ts));
		vxcross((*ss),(*ts),(*ns));
	}
	else
	{
		coordinate_system_by_normal(ns,ss,ts);
	}
}
INLINE void _get_dpdu_dpdv(
		const triangle_t* triangle, float du1,float du2,float dv1,float dv2,
		//out
		vector3f_t *dpdu,
		vector3f_t *dpdv
		)
{
	///get dpdu, dpdv
	vector3f_t e1,e2;
	vsub(e1,triangle->p1,triangle->p0);
	vsub(e2,triangle->p2,triangle->p0);

	vector3f_t dp1,dp2;
	vclr(dp1);vclr(dp2);
	vsub(dp1,triangle->p0, triangle->p2);
	vsub(dp2,triangle->p1, triangle->p2);
	float determinant = du1 * dv2 - dv1*du2;
//	if(determinant == 0.f)
	{
		vector3f_t v1;
		vclr(v1);
		vxcross(v1,e2,e1);
		vnorm(v1);
		coordinate_system(&v1,dpdu,dpdv);
	}
//	else
	if(determinant != 0.f)
	{
		float inv_det = 1.f/ determinant;
		vector3f_t tmp0,tmp1;
		vclr(tmp0);vclr(tmp1);

		vsmul(tmp0,dv2,dp1);
		vsmul(tmp1,dv1,dp2);
		vsub((*dpdu),tmp0,tmp1);
		vsmul((*dpdu),inv_det,(*dpdu));

		vsmul(tmp0,(du2),dp1);
		vsmul(tmp1,(du1),dp2);
		vsub((*dpdv),tmp1,tmp0);
		vsmul((*dpdv),inv_det,(*dpdv));
	}


}
INLINE void _get_dndu_dndv(
		GLOBAL const float* triangle_mesh_memory,unsigned vertex_format,const triangle_t* triangle, float b1,float b2,unsigned normal_start,
		float du1,float du2,float dv1,float dv2,
		const transform_t* object_to_world,
		//out
		vector3f_t* dpdu,
		vector3f_t* dpdv,
		vector3f_t* dndu,
		vector3f_t* dndv,
		normal3f_t* ns
		)
{
	vclr((*dndu));
	vclr((*dndv));

	_get_dpdu_dpdv(triangle,du1,du2,dv1,dv2,dpdu,dpdv);

	vector3f_t n0,n1,n2;
	vclr(n0);
	vclr(n1);
	vclr(n2);
	//todo auto generate ?
	vxcross((*ns),(*dpdu),(*dpdv));
	vnorm((*ns));
	//clear triangle data
	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL))
	{
		//load normals
		//
	
		vector3f_t n;
		vclr(n);

		n0.x = triangle_mesh_memory[3*triangle->v0_index + normal_start];
		n0.y = triangle_mesh_memory[3*triangle->v0_index + 1 + normal_start];
		n0.z = triangle_mesh_memory[3*triangle->v0_index + 2 + normal_start];

		n1.x = triangle_mesh_memory[3*triangle->v1_index + normal_start];
		n1.y = triangle_mesh_memory[3*triangle->v1_index + 1 + normal_start];
		n1.z = triangle_mesh_memory[3*triangle->v1_index + 2 + normal_start];

		n2.x = triangle_mesh_memory[3*triangle->v2_index + normal_start];
		n2.y = triangle_mesh_memory[3*triangle->v2_index + 1 + normal_start];
		n2.z = triangle_mesh_memory[3*triangle->v2_index + 2 + normal_start];
		//transform_normal(n2,object_to_world,n);
		float b0 = 1.f-b1-b2;
		vsmul(n0,b0,n0);
		vsmul(n1,b1,n1);
		vsmul(n2,b2,n2);
		vadd(n,n0,n1);
		vadd(n,n,n2);
		transform_normal((*ns),(*object_to_world),n);
		vnorm((*ns));
	}
	float determinant = du1 * dv2 - dv1*du2;
	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL))
	{
		if (determinant != 0.f)
		{
			float inv_det = 1.f/determinant;
			normal3f_t dn1,dn2;
			vsub(dn1,n0,n2);
			vsub(dn2,n1,n2);

			normal3f_t tmp1,tmp2;
			vsmul(tmp1,dv2, dn1);
			vsmul(tmp2,dv1, dn2);
			vsub((*dndu),tmp1,tmp2);
			

			vsmul(tmp1,du1,dn2);
			vsmul(tmp2,du2,dn1);
			vsub((*dndv),tmp1,tmp2);

			vsmul(tmp1,inv_det,(*dndu));
			vsmul(tmp2,inv_det,(*dndv));
			transform_normal((*dndu),(*object_to_world),tmp1);
			transform_normal((*dndv),(*object_to_world),tmp2);
		}
	}

}
INLINE void get_triangle_shading_info_global(GLOBAL const float* triangle_mesh_memory,const triangle_t* triangle,unsigned vertex_format, float b1,float b2,point3f_t p,differential_geometry_t *dg)
{
	transform_t object_to_world;
	unsigned normal_start,tangent_start,uvs_start;
	{
		//load data from memory
		unsigned transform_start = 3;
		transform_init((object_to_world),
				triangle_mesh_memory[transform_start],triangle_mesh_memory[transform_start+1],triangle_mesh_memory[transform_start+2],triangle_mesh_memory[transform_start+3],
				triangle_mesh_memory[transform_start+4],triangle_mesh_memory[transform_start+5],triangle_mesh_memory[transform_start+6],triangle_mesh_memory[transform_start+7],
				triangle_mesh_memory[transform_start+8],triangle_mesh_memory[transform_start+9],triangle_mesh_memory[transform_start+10],triangle_mesh_memory[transform_start+11],
				triangle_mesh_memory[transform_start+12],triangle_mesh_memory[transform_start+13],triangle_mesh_memory[transform_start+14],triangle_mesh_memory[transform_start+15])

		unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
		unsigned number_vertex = as_uint(triangle_mesh_memory[1]);

		unsigned point_start = transform_start+16;//add transform_data
		normal_start = point_start + 3 * number_vertex;
		tangent_start = normal_start;
		uvs_start = tangent_start;

		if(GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL)
			tangent_start = normal_start + 3 * number_vertex;
		if(GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_TANGENT)
			uvs_start = tangent_start + 3*number_vertex;
	}

	///////////////////////////////////////////////
	{
		float tu,tv,du1,du2,dv1,dv2;
		_get_tu_tv(triangle_mesh_memory,vertex_format,triangle,b1,b2,uvs_start,&tu,&tv,&du1,&du2,&dv1,&dv2);

		vector3f_t dndu,dndv,ss,ts;
		{
			vector3f_t dpdu,dpdv;
			normal3f_t ns;
			_get_dndu_dndv(triangle_mesh_memory,vertex_format,triangle,b1,b2,normal_start,du1,du2,dv1,dv2,&object_to_world,
					&dpdu,&dpdv,&dndu,&dndv,&ns);

			_get_ss_ts(triangle_mesh_memory, vertex_format,triangle, b1, b2,tangent_start,
					du1,du2,dv1,dv2,
					tu,tv,&ns,
					&dpdu,
					&object_to_world,
					&ts,
					&ss);
		}

		differential_geometry_init(*dg,p,ss,ts,dndu,dndv,tu,tv,triangle);
		return;
	}
}

INLINE void get_triangle_shading_info(GLOBAL const float* triangle_mesh_memory,const triangle_t* triangle,unsigned vertex_format, float b1,float b2,point3f_t p,differential_geometry_t *dg)
{
	transform_t object_to_world;
	unsigned normal_start,tangent_start,uvs_start;
	{
		//load data from memory
		unsigned transform_start = 3;
		transform_init((object_to_world),
				triangle_mesh_memory[transform_start],triangle_mesh_memory[transform_start+1],triangle_mesh_memory[transform_start+2],triangle_mesh_memory[transform_start+3],
				triangle_mesh_memory[transform_start+4],triangle_mesh_memory[transform_start+5],triangle_mesh_memory[transform_start+6],triangle_mesh_memory[transform_start+7],
				triangle_mesh_memory[transform_start+8],triangle_mesh_memory[transform_start+9],triangle_mesh_memory[transform_start+10],triangle_mesh_memory[transform_start+11],
				triangle_mesh_memory[transform_start+12],triangle_mesh_memory[transform_start+13],triangle_mesh_memory[transform_start+14],triangle_mesh_memory[transform_start+15])

		unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
		unsigned number_vertex = as_uint(triangle_mesh_memory[1]);

		unsigned point_start = transform_start+16;//add transform_data
		normal_start = point_start + 3 * number_vertex;
		tangent_start = normal_start;
		uvs_start = tangent_start;

		if(GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL)
			tangent_start = normal_start + 3 * number_vertex;
		if(GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_TANGENT)
			uvs_start = tangent_start + 3*number_vertex;
	}

	///////////////////////////////////////////////
	{
		float tu,tv,du1,du2,dv1,dv2;
		_get_tu_tv(triangle_mesh_memory,vertex_format,triangle,b1,b2,uvs_start,&tu,&tv,&du1,&du2,&dv1,&dv2);

		vector3f_t dndu,dndv,ss,ts;
		{
			vector3f_t dpdu,dpdv;
			normal3f_t ns;
			_get_dndu_dndv(triangle_mesh_memory,vertex_format,triangle,b1,b2,normal_start,du1,du2,dv1,dv2,&object_to_world,
					&dpdu,&dpdv,&dndu,&dndv,&ns);

			_get_ss_ts(triangle_mesh_memory, vertex_format,triangle, b1, b2,tangent_start,
					du1,du2,dv1,dv2,
					tu,tv,&ns,
					&dpdu,
					&object_to_world,
					&ts,
					&ss);
		}

		differential_geometry_init(*dg,p,ss,ts,dndu,dndv,tu,tv,triangle);
		return;
	}

	/*
	///////////////////////////////////////////////////
	unsigned v0_index = triangle->v0_index;
	unsigned v1_index = triangle->v1_index;
	unsigned v2_index = triangle->v2_index;

	float uv0[2],uv1[2],uv2[2];
	uv0[0] = 0.f; uv0[1] = 0.f;
	uv1[0] = 1.f; uv1[1] = 0.f;
	uv2[0] = 1.f; uv2[1] = 1.f;
	if( 0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_UVS))
	{
		//load uvs
		uv0[0] = triangle_mesh_memory[2*v0_index + uvs_start];
		uv0[1] = triangle_mesh_memory[2*v0_index + 1 + uvs_start];

		uv1[0] = triangle_mesh_memory[2*v1_index + uvs_start];
		uv1[1] = triangle_mesh_memory[2*v1_index + 1 + uvs_start];

		uv2[0] = triangle_mesh_memory[2*v2_index + uvs_start];
		uv2[1] = triangle_mesh_memory[2*v2_index + 1 + uvs_start];
	}
	/////////////////////////////////////////////////////////////////////////////////

	//get shading info
	vector3f_t dpdu,dpdv;
	vclr(dpdu);vclr(dpdv);
	//float uvs[3][2];
	//get_uvs(triangle,uvs);

	//
	
	float du1 = uv0[0] - uv2[0];
	float du2 = uv1[0] - uv2[0];
	float dv1 = uv0[1] - uv2[1];
	float dv2 = uv1[1] - uv2[1];

	vector3f_t e1,e2;
	vsub(e1,triangle->p1,triangle->p0);
	vsub(e2,triangle->p2,triangle->p0);

	vector3f_t dp1,dp2;
	vclr(dp1);vclr(dp2);
	vsub(dp1,triangle->p0, triangle->p2);
	vsub(dp2,triangle->p1, triangle->p2);
	float determinant = du1 * dv2 - dv1*du2;
	float inv_det;
	if(determinant == 0.f)
	{
		vector3f_t v1;
		vclr(v1);
		vxcross(v1,e2,e1);
		vnorm(v1);
		coordinate_system(&v1,&dpdu,&dpdv);
	}
	else{
		inv_det = 1.f/ determinant;
		vector3f_t tmp0,tmp1;
		vclr(tmp0);vclr(tmp1);

		vsmul(tmp0,dv2,dp1);
		vsmul(tmp1,dv1,dp2);
		vsub(dpdu,tmp0,tmp1);
		vsmul(dpdu,inv_det,dpdu);

		vsmul(tmp0,(du2),dp1);
		vsmul(tmp1,(du1),dp2);
		vsub(dpdv,tmp1,tmp0);
		vsmul(dpdv,inv_det,dpdv);
	}
	float b0 = 1-b1-b2;
	float tu = b0 * uv0[0] + b1*uv1[0] + b2*uv2[0];
	float tv = b0 * uv0[1] + b1*uv1[1] + b2*uv2[1];
	////////////////////////////////////////////////////////////////////////////

	//get dgshading 
	normal3f_t ns;

	vector3f_t n0,n1,n2;
	vclr(n0);
	vclr(n1);
	vclr(n2);
	//todo auto generate ?
	vxcross(ns,dpdu,dpdv);
	vnorm(ns);
	//clear triangle data
	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL))
	{
		//load normals
		//
	
		vector3f_t n;
		vclr(n);

		n0.x = triangle_mesh_memory[3*v0_index + normal_start];
		n0.y = triangle_mesh_memory[3*v0_index + 1 + normal_start];
		n0.z = triangle_mesh_memory[3*v0_index + 2 + normal_start];

		n1.x = triangle_mesh_memory[3*v1_index + normal_start];
		n1.y = triangle_mesh_memory[3*v1_index + 1 + normal_start];
		n1.z = triangle_mesh_memory[3*v1_index + 2 + normal_start];

		n2.x = triangle_mesh_memory[3*v2_index + normal_start];
		n2.y = triangle_mesh_memory[3*v2_index + 1 + normal_start];
		n2.z = triangle_mesh_memory[3*v2_index + 2 + normal_start];
		//transform_normal(n2,object_to_world,n);
		vsmul(n0,b0,n0);
		vsmul(n1,b1,n1);
		vsmul(n2,b2,n2);
		vadd(n,n0,n1);
		vadd(n,n,n2);
		transform_normal(ns,object_to_world,n);
		vnorm(ns);
	}
	//////////////////////////////
	vector3f_t ss,ts;
	ss = dpdu;
	vnorm(ss);
	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_TANGENT))
	{
		//load tangent
		vector3f_t s0,s1,s2;
		vclr(s0);
		vclr(s1);
		vclr(s2);

		vector3f_t s;
		vclr(s);
		s0.x = triangle_mesh_memory[3*v0_index + tangent_start];
		s0.y = triangle_mesh_memory[3*v0_index + 1 + tangent_start];
		s0.z = triangle_mesh_memory[3*v0_index + 2 + tangent_start];
		//transform_vector((s0),object_to_world,s);

		s1.x = triangle_mesh_memory[3*v1_index + tangent_start];
		s1.y = triangle_mesh_memory[3*v1_index + 1 + tangent_start];
		s1.z = triangle_mesh_memory[3*v1_index + 2 + tangent_start];
		//transform_vector((s1),object_to_world,s);

		s2.x = triangle_mesh_memory[3*v2_index + tangent_start];
		s2.y = triangle_mesh_memory[3*v2_index + 1 + tangent_start];
		s2.z = triangle_mesh_memory[3*v2_index + 2 + tangent_start];
		//transform_vector((s2),object_to_world,s);
		vsmul(s0,b0,s0);
		vsmul(s1,b1,s1);
		vsmul(s2,b2,s2);
		vadd(s,s0,s1);
		vadd(s,s,s2);
		transform_vector(ss,object_to_world,s);
		vnorm(ss);
	}
	/////////////////////////////////////////////////////////////////////
	
	vxcross(ts,ss,ns);
	if(vdot(ts,ts) > 0.f)
	{
		vnorm(ts);
		vxcross(ss,ts,ns);
	}
	else
	{
		coordinate_system_by_normal(&ns,&ss,&ts);
	}

	//differential_geometry_init(dg,)
	vector3f_t dndu,dndv;
	vclr(dndu);
	vclr(dndv);


	if(0 != (GetShapeExtraInfo(vertex_format) & TRIANGLE_VERTEX_INDEX_NORMAL))
	{
		if (determinant != 0.f)
		{
			normal3f_t dn1,dn2;
			vsub(dn1,n0,n2);
			vsub(dn2,n1,n2);

			normal3f_t tmp1,tmp2;
			vsmul(tmp1,dv2, dn1);
			vsmul(tmp2,dv1, dn2);
			vsub(dndu,tmp1,tmp2);
			

			vsmul(tmp1,du1,dn2);
			vsmul(tmp2,du2,dn1);
			vsub(dndv,tmp1,tmp2);

			vsmul(tmp1,inv_det,dndu);
			vsmul(tmp2,inv_det,dndv);
			transform_normal(dndu,object_to_world,tmp1);
			transform_normal(dndv,object_to_world,tmp2);
		}
	}

	//get_ss_ts()
	//get_dndu_dndv()
	//get_tu_tv()
	differential_geometry_init(*dg,p,ss,ts,dndu,dndv,tu,tv,triangle);
	*/
}

INLINE void load_triangle_vertex8(GLOBAL const float* triangle_mesh_memory,unsigned triangle_index,unsigned vertex_format,triangle_t *triangle)
{
	unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
	unsigned number_vertex = as_uint(triangle_mesh_memory[1]);
	unsigned transform_start = 3;
	unsigned point_start = transform_start+16;//add transform_data
	unsigned normal_start = point_start + 3 * number_vertex;
	unsigned tangent_start = normal_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_NORMAL )== 0)
		tangent_start = normal_start;
	unsigned uvs_start = tangent_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_TANGENT) == 0)
		uvs_start = tangent_start;
	unsigned vertex_index_start = uvs_start + 2*number_vertex +  (3 * triangle_index) / 4;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_UVS) == 0)
		vertex_index_start = uvs_start + (3*triangle_index)/4;

	unsigned vertex_index = as_uint(triangle_mesh_memory[vertex_index_start]);
	unsigned vertex_index_next = as_uint(triangle_mesh_memory[vertex_index_start + 1]);

	unsigned char v_index[] = {
		(unsigned char)(vertex_index & 0x000000ff),
		(unsigned char)((vertex_index & 0x0000ff00) >> 8),
		(unsigned char)((vertex_index & 0x00ff0000) >> 16),
		(unsigned char)((vertex_index & 0xff000000) >> 24),
		(unsigned char)((vertex_index_next & 0x000000ff)),
		(unsigned char)((vertex_index_next & 0x0000ff00) >> 8)
	};
	unsigned char v0_index = v_index[(3*triangle_index)%4];
	unsigned char v1_index = v_index[(3*triangle_index)%4+1];
	unsigned char v2_index = v_index[(3*triangle_index)%4+2];
	load_triangle_only_point_data_(triangle_mesh_memory,v0_index,v1_index,v2_index,triangle);
}


INLINE void load_triangle_vertex16(GLOBAL const float* triangle_mesh_memory,unsigned triangle_index,unsigned vertex_format,triangle_t *triangle)
{
	unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
	unsigned number_vertex = as_uint(triangle_mesh_memory[1]);
	unsigned transform_start = 3;
	unsigned point_start = transform_start+16;//add transform_data
	unsigned normal_start = point_start + 3 * number_vertex;
	unsigned tangent_start = normal_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_NORMAL )== 0)
		tangent_start = normal_start;
	unsigned uvs_start = tangent_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_TANGENT) == 0)
		uvs_start = tangent_start;
	unsigned vertex_index_start = uvs_start + 2*number_vertex +  (3 * triangle_index) / 2;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_UVS) == 0)
		vertex_index_start = uvs_start + (3*triangle_index)/2;

	unsigned vertex_index = as_uint(triangle_mesh_memory[vertex_index_start]);
	unsigned vertex_index_next = as_uint(triangle_mesh_memory[vertex_index_start + 1]);

	unsigned short v_index[] = {
		(unsigned short)(vertex_index & 0x0000ffff),
		(unsigned short)((vertex_index & 0xffff0000) >> 16),
		(unsigned short)((vertex_index_next & 0x0000ffff)),
		(unsigned short)((vertex_index_next & 0xffff0000) >> 16)
	};
	unsigned short v0_index = v_index[(3*triangle_index)%2];
	unsigned short v1_index = v_index[(3*triangle_index)%2+1];
	unsigned short v2_index = v_index[(3*triangle_index)%2+2];
	load_triangle_only_point_data_(triangle_mesh_memory,v0_index,v1_index,v2_index,triangle);
}

INLINE void load_triangle_vertex32(GLOBAL const float* triangle_mesh_memory,unsigned triangle_index,unsigned vertex_format,triangle_t *triangle)
{
	unsigned number_triangles = as_uint(triangle_mesh_memory[0]);
	unsigned number_vertex = as_uint(triangle_mesh_memory[1]);
	unsigned transform_start = 3;
	unsigned point_start = transform_start+16;//add transform_data
	unsigned normal_start = point_start + 3 * number_vertex;
	unsigned tangent_start = normal_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_NORMAL )== 0)
		tangent_start = normal_start;
	unsigned uvs_start = tangent_start + 3 * number_vertex;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_TANGENT) == 0)
		uvs_start = tangent_start;
	unsigned vertex_index_start = uvs_start + 2*number_vertex +  (3 * triangle_index) ;
	if((vertex_format & TRIANGLE_VERTEX_INDEX_UVS) == 0)
		vertex_index_start = uvs_start + (3*triangle_index);

	unsigned vertex_index0 = as_uint(triangle_mesh_memory[vertex_index_start]);
	unsigned vertex_index1 = as_uint(triangle_mesh_memory[vertex_index_start + 1]);
	unsigned vertex_index2 = as_uint(triangle_mesh_memory[vertex_index_start + 2]);
	 
	load_triangle_only_point_data_(triangle_mesh_memory,vertex_index0,vertex_index1,vertex_index2,triangle);
}

INLINE GLOBAL const float* load_triangle_points(GLOBAL const float* triangle_mesh_data,shape_info_t shape_info, triangle_t* triangle)
{
	const unsigned memory_start = shape_info.memory_start;
	const unsigned mesh_memory_start = as_uint(triangle_mesh_data[memory_start]);
	const unsigned triangle_index = as_uint(triangle_mesh_data[memory_start + 1]);
							
	switch(GetShapeIndexType(shape_info))
	{
		case TRIANGLE_VERTEX8:
		{
			load_triangle_vertex8(triangle_mesh_data+mesh_memory_start,triangle_index,GetShapeExtraInfo(shape_info.shape_type),triangle);
			break;
		}
		case TRIANGLE_VERTEX16:
			{
				load_triangle_vertex16(triangle_mesh_data + mesh_memory_start, triangle_index,GetShapeExtraInfo(shape_info.shape_type), triangle);
				break;
			}
		case TRIANGLE_VERTEX32:
			{
				load_triangle_vertex32(triangle_mesh_data + mesh_memory_start, triangle_index,GetShapeExtraInfo(shape_info.shape_type), triangle);
				break;
			}
	}
	return triangle_mesh_data + mesh_memory_start;
}


//bool intersect_triangle(GLOBAL float* triangle_data)
INLINE int intersect_triangleP(const triangle_t *triangle,
					  ray_t *r
					  )
{
	vector3f_t e1,e2;
	vsub(e1,triangle->p1,triangle->p0);
	vsub(e2,triangle->p2,triangle->p0);
	vector3f_t s1;
	vxcross(s1,r->d,e2);
	float divisor = vdot(s1, e1);

	if(divisor == 0.f) return 0;
	float inv_divisor = 1.f/divisor;

	vector3f_t d;
	vsub(d,r->o, triangle->p0);
	float b1 = vdot(d,s1) * inv_divisor;
	if (b1<0 || b1 > 1)
		return 0;

	vector3f_t s2;
	vxcross(s2,d,e1);
	float b2 = vdot(r->d, s2) * inv_divisor;
	if(b2 < 0.f || b1 + b2 > 1.f)
		return 0;

	float t = vdot(e2,s2) * inv_divisor;
	if(t < r->mint || t > r->maxt)
		return 0;

	return 1;
}

INLINE void get_uvs(const triangle_t *triangle,float uvs[3][2])
{
	/*
	uvs[0][0] = triangle->uv0[0];
	uvs[0][1] = triangle->uv0[1];	

	uvs[1][0] = triangle->uv1[0];
	uvs[1][1] = triangle->uv1[1];	

	uvs[2][0] = triangle->uv2[0];
	uvs[2][1] = triangle->uv2[1];	
	*/

	uvs[0][0] = 0.f; uvs[0][1] = 0.f;
	uvs[1][0] = 1.f; uvs[1][1] = 0.f;
	uvs[2][0] = 1.f; uvs[2][1] = 1.f;
}
INLINE int intersect_triangle_simple(GLOBAL const float* triangle_mesh_memory,unsigned vertex_format, const triangle_t *triangle,
					 ray_t *r,//inout
					 //out
					 float *t,
					 float *b1,float *b2)
{
	vector3f_t e1,e2;
	vclr(e1);vclr(e2);
	vsub(e1,triangle->p1, triangle->p0);
	vsub(e2,triangle->p2, triangle->p0);
	vector3f_t s1;
	vclr(s1);
	vxcross(s1,r->d, e2);
	s1.w = 0;
	float divisor = vdot(s1,e1);

	if (divisor == 0.f)
		return 0;
	float inv_divisor = 1.f/divisor;

	vector3f_t d;
	vclr(d);
	vsub(d,r->o, triangle->p0);
	*b1 = vdot(d,s1) * inv_divisor;
	if (*b1<0 || *b1 > 1)
		return 0;
	vector3f_t s2;
	vclr(s2);
	vxcross(s2,d,e1);
	*b2 = vdot(r->d, s2) * inv_divisor;
	if( *b2 < 0.f || *b1 + *b2 > 1.f)
		return 0;

	float t_hit = vdot(e2,s2) * inv_divisor;
	if(t_hit < r->mint || t_hit > r->maxt)
		return 0;
	*t = t_hit;
	return 1;
}
INLINE int intersect_triangle(GLOBAL const float* triangle_mesh_memory,unsigned vertex_format, const triangle_t *triangle,
					 ray_t *r,
					 float *tHit,
					 differential_geometry_t *dg,
					 float *epsilon)
{
	float b1,b2;
	if(1 == intersect_triangle_simple(triangle_mesh_memory,vertex_format,triangle,r,tHit,&b1,&b2))
	{
		point3f_t p;
		vclr(p);
		rpos(p,(*r),(*tHit));
		get_triangle_shading_info(triangle_mesh_memory, triangle, vertex_format, b1,b2,p, dg);
		*epsilon = 1e-3f * *tHit;
		return 1;
	}
	return 0;
}

INLINE float triangle_area(triangle_t *triangle)
{
	vector3f_t e1,e2;
	vsub(e1,triangle->p1, triangle->p0);
	vsub(e2,triangle->p2, triangle->p0);
	vector3f_t v_norm;
	vxcross(v_norm,e1,e2);
	return 0.5f * sqrt(vdot(v_norm,v_norm));
}

INLINE void triangle_sample(triangle_t* triangle, float u1,float u2, normal3f_t *ns, point3f_t *point)
{
	float b1,b2;
	uniform_sample_triangle(u1,u2,&b1,&b2);

	point3f_t p,p_tmp;
	vsmul(p_tmp,b1,triangle->p0);
	vsmul(p,b2,triangle->p1);
	vadd(p,p,p_tmp);
	vsmul(p_tmp,(1.f-b1-b2),triangle->p2);
	vadd(p,p, p_tmp);
	vassign((*point),p);
	
	vector3f_t e1,e2;
	vsub(e1, triangle->p1,triangle->p0);
	vsub(e2, triangle->p2,triangle->p0);
	vxcross(*ns, e1,e2);
	vnorm(*ns);
	if(triangle->reverse_orientation) vneg(*ns,*ns);
	return;
}

#endif
